package stock

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"time"

	"mall/common/cachex"
	"mall/common/modelx"
	"mall/service/backend/common/i18n"
	"mall/service/product/model"
	"mall/service/product/rpc/internal/svc"
	"mall/service/product/rpc/internal/utils/errorhandler"
	"mall/service/product/rpc/types/product"

	"github.com/sirupsen/logrus"
	"github.com/volatiletech/null/v8"
	"github.com/zeromicro/go-zero/core/logx"
)

type UpdateProductStockLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewUpdateProductStockLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateProductStockLogic {
	return &UpdateProductStockLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *UpdateProductStockLogic) UpdateProductStock(in *product.UpdateProductStockReq) (*product.BaseMsg, error) {
	// logrus.Infof(fmt.Sprintf("[ERP] UpdateProductStock in: %+v", in.Data))
	// map[98:10]
	if len(in.Data) == 0 {
		return &product.BaseMsg{}, nil
	}

	// var skuCodeStockMap = make(map[string]uint64)
	// var skuCodes []any
	// for skuCode, stock := range in.Data {
	// 	code := strings.TrimSpace(skuCode)
	// 	if len(code) > 0 {
	// 		if _, ok := skuCodeStockMap[code]; !ok {
	// 			skuCodes = append(skuCodes, code)
	// 			skuCodeStockMap[code] = stock
	// 		}
	// 	}
	// }

	// if len(skuCodes) == 0 {
	// 	logx.Errorw("[ERP] UpdateProductStock Error: skuCodes is empty")
	// 	return &product.BaseMsg{}, nil
	// }

	// filterMap := make(map[string]map[string][]any)
	// filterMap[model.ProductVariationColumns.SkuCode] = map[string][]any{
	// 	"in": skuCodes,
	// }
	// list, _, err := l.svcCtx.CurrentProduct.GetVariationList(l.ctx, model.M{}, -1, filterMap)
	// if err != nil {
	// 	logx.Errorw("[ERP] UpdateProductStock GetVariationList Error:", logx.Field("detail", err.Error()))
	// 	return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	// }

	// if len(list) == 0 {
	// 	logx.Errorw("[ERP] UpdateProductStock GetVariationList Error: list is empty")
	// 	return &product.BaseMsg{}, nil
	// }

	var idStockMap = make(map[uint64]uint64)
	var pids []any
	// for _, v := range list {
	// 	if _, ok := idStockMap[v.ProductVariationID]; !ok {
	// 		if stock, ok := skuCodeStockMap[v.SkuCode]; ok {
	// 			idStockMap[v.ProductVariationID] = stock
	// 			pids = append(pids, v.ProductVariationID)
	// 		}
	// 	}
	// }
	for skuId, stock := range in.Data {
		if id := strings.TrimSpace(skuId); len(id) > 0 {
			if i, err := strconv.ParseInt(id, 10, 64); err == nil {
				if _, ok := idStockMap[uint64(i)]; !ok {
					pids = append(pids, uint64(i))
					idStockMap[uint64(i)] = stock
				}
			}
		}
	}
	if len(pids) == 0 {
		return &product.BaseMsg{}, nil
	}

	metaFilterMap := make(map[string]map[string][]any)
	metaFilterMap[model.ProductVariationMetumColumns.ProductID] = map[string][]any{
		"in": pids,
	}
	metaFilterMap[model.ProductVariationMetumColumns.MetaKey] = map[string][]any{
		"equalTo": {"_stock"},
	}
	meta, _, err := l.svcCtx.CurrentProduct.GetVariationMeta(l.ctx, model.M{}, -1, metaFilterMap)
	if err != nil {
		logx.Errorw("[ERP] UpdateProductStock GetVariationMeta Error:", logx.Field("detail", err.Error()))
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	var newMeta []*model.ProductVariationMetum
	if len(meta) > 0 {
		for _, v := range meta {
			if v.ProductID > 0 {
				if stock, ok := idStockMap[v.ProductID]; ok {
					v.MetaValue = null.StringFrom(strconv.FormatUint(stock, 10))
					newMeta = append(newMeta, v)
					//删除已修改的项
					delete(idStockMap, v.ProductID)
				}
			}
		}
	}

	//剩余的是添加的项
	if len(idStockMap) > 0 {
		for id, stock := range idStockMap {
			newMeta = append(newMeta, &model.ProductVariationMetum{
				ProductID: id,
				MetaKey:   "_stock",
				MetaValue: null.StringFrom(strconv.FormatUint(stock, 10)),
			})
		}
	}

	tx, err := l.svcCtx.DB.BeginTx(l.ctx, nil)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	err = l.svcCtx.CurrentProduct.UpdateProductVariationMeta(l.ctx, tx, newMeta, nil)
	if err != nil {
		tx.Rollback()
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	tx.Commit()

	err = l.updateProductStock(pids)
	if err != nil {
		logx.Errorw("[ERP] updateProductStock Error:", logx.Field("detail", err.Error()))
	}

	err = l.updateProductVariationStock(newMeta)
	if err != nil {
		logx.Errorw("[ERP] updateProductStock Error:", logx.Field("detail", err.Error()))
	}

	return &product.BaseMsg{
		Msg: i18n.UpdateSuccess,
	}, nil
}

func (l *UpdateProductStockLogic) updateProductStock(vals []any) error {
	filterMap := make(map[string]map[string][]any)
	filterMap[model.ProductVariationColumns.ProductVariationID] = map[string][]any{
		"in": vals,
	}
	res, _, err := l.svcCtx.CurrentProduct.GetVariationList(l.ctx, model.M{}, -1, filterMap) // 根据skuId找productId
	if err != nil {
		return err
	}

	if len(res) > 0 {
		var pids []any
		for _, v := range res {
			if v.ProductID > 0 {
				pids = append(pids, v.ProductID)
			}
		}

		if len(pids) > 0 {
			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ProductVariationColumns.ProductID] = map[string][]any{
				"in": pids,
			}
			list, _, err := l.svcCtx.CurrentProduct.GetVariationList(l.ctx, model.M{}, -1, subFilterMap) // 根据productId找所有skuId
			if err != nil {
				return err
			}

			if len(list) > 0 {
				var skuIds []any
				skuIdMap := make(map[uint64]uint64)
				for _, v := range list {
					skuIds = append(skuIds, v.ProductVariationID)
					skuIdMap[v.ProductVariationID] = v.ProductID
				}

				skuMetaFilterMap := make(map[string]map[string][]any)
				skuMetaFilterMap[model.ProductVariationMetumColumns.ProductID] = map[string][]any{
					"in": skuIds,
				}
				skuMetaFilterMap[model.ProductVariationMetumColumns.MetaKey] = map[string][]any{
					"equalTo": {"_stock"},
				}
				skuMetaList, _, err := l.svcCtx.CurrentProduct.GetVariationMeta(l.ctx, model.M{}, -1, skuMetaFilterMap)
				if err != nil {
					return err
				}

				stockMap := make(map[uint64]int64)
				if len(skuMetaList) > 0 {
					for _, v := range skuMetaList {
						if strings.TrimSpace(v.MetaKey) == "_stock" && v.MetaValue.Valid {
							if i, err := strconv.ParseInt(v.MetaValue.String, 10, 32); err == nil {
								if pid, ok := skuIdMap[v.ProductID]; ok {
									if stock, ok := stockMap[pid]; !ok {
										stockMap[pid] = i
									} else {
										stockMap[pid] = stock + i
									}
								}
							}
						}
					}

					nowString := modelx.FormatTime(time.Now(), "")
					for pid, stock := range stockMap {
						err = l.svcCtx.CurrentProduct.UpdateMetum(l.ctx, nil, &model.ProductMetum{
							ProductID: pid,
							MetaKey:   "_stock",
							MetaValue: null.StringFrom(strconv.FormatInt(stock, 10)),
						})
						if err != nil {
							return err
						}

						err = l.svcCtx.CurrentProduct.UpdateMetum(l.ctx, nil, &model.ProductMetum{
							ProductID: pid,
							MetaKey:   "_sync_from",
							MetaValue: null.StringFrom(nowString),
						})
						if err != nil {
							return err
						}
					}
				}
			}
		}
	}

	return nil
}

func (l *UpdateProductStockLogic) updateProductVariationStock(meta []*model.ProductVariationMetum) error {
	if len(meta) > 0 {
		for _, item := range meta {
			key := strings.TrimSpace(item.MetaKey)
			if len(key) > 0 && item.MetaValue.Valid {
				val := strings.TrimSpace(item.MetaValue.String)

				if key == "_stock" && item.ProductID > 0 {
					if i, err := strconv.ParseInt(val, 10, 32); err == nil {
						stockKey := l.getStockKey(item.ProductID)
						err = l.svcCtx.BizRedis.SetCtx(l.ctx, stockKey, strconv.FormatInt(i, 10))
						if err != nil {
							logrus.Info(fmt.Sprintf("UpdateProductStock SetCtx err: %+v", err))
						}

						stockFrozenKey := l.getStockFrozenKey(item.ProductID)
						_, err = l.svcCtx.BizRedis.SetnxCtx(l.ctx, stockFrozenKey, "0")
						if err != nil {
							logrus.Info(fmt.Sprintf("UpdateProductStock SetnxCtx frozen err: %+v", err))
						}
					}
				}
			}
		}
	}

	return nil
}

func (l *UpdateProductStockLogic) getStockKey(vid uint64) string {
	return fmt.Sprintf("{%s}:%d", cachex.SkuStock, vid)
}

func (l *UpdateProductStockLogic) getStockFrozenKey(vid uint64) string {
	return fmt.Sprintf("{%s}:%d:%s", cachex.SkuStock, vid, cachex.SkuStockFrozen)
}
